// #ifndef __M__SING_HPP_
// #define __M__SING_HPP_

// // 日志落地模块的实现

// #include "util.hpp"
// #include <memory>
// #include <fstream>
// #include <cassert>
// #include <sstream>

// namespace lyzlog
// {
//     // 日志落地基类
//     class LogSink
//     {
//     public:
//         using ptr = std::shared_ptr<LogSink>;
//         LogSink();
//         virtual ~LogSink();
//         virtual void log(const std::string data, size_t len) = 0;
//     };

//     // 2.派生子类
//     // 标准输出
//     class StdoutSink : public LogSink
//     {
//     public:
//         void log(const char *data, size_t len)
//         {
//             // TODO  std::out <<  VS std::cout.write 区别？？
//             std::cout.write(data, len);
//         }
//     };

//     // 指定文件
//     class FileSink : public LogSink
//     {
//     public:
    
//         using ptr = std::shared_ptr<FileSink>;
//         FileSink(const std::string &pathname) : _pathname(pathname)
//         {
//             // 1.创建日志所在的路径
//             util::File::createDirectory(util::File::path(pathname));
//             // 2.创建并打开日志文件
//             // 以二进制方式打开文件，并且以追加的方式写入文件
//             _ofs.open(_pathname, std::ios::binary | std::ios::app);
//             assert(_ofs.is_open());
//         }
//         void log(const char *data, size_t len)
//         {
//             _ofs.write(data, len);
//             assert(_ofs.good()); // good 用于检查文件流是否正常 如果失败返回false
//         }

//     private:
//         std::string _pathname; // 指定输出文件名称
//         std::ofstream _ofs;    // 控制文件的句柄
//     };

//     // 滚动文件（按照文件大小的滚动文件
//     class RollBySizeSink : public LogSink
//     {
//     public:
//         using ptr = std::shared_ptr<RollBySizeSink>;

//         RollBySizeSink(const std::string &basename, size_t max_size) : _basename(basename), _max_fsize(max_size)
//         {
//             std::string pathname = CreateNewFile();
//             // 1.创建日志所在目录
//             util::File::createDirectory(util::File::path(pathname));
//             // 2.创建并打开日志文件
//             // 以二进制方式打开文件，并且以追加的方式写入文件
//             _ofs.open(pathname, std::ios::binary | std::ios::app);
//             assert(_ofs.is_open());
//         }

//         void log(const char *data, size_t len)
//         {
//             // 判断滚动文件大小是否满足要求
//             if (_cur_fsize >= _max_fsize)
//             {
//                 //!!!!! 关闭之前的文件，防止内存泄漏
//                 _ofs.close();
//                 std::string pathname = CreateNewFile();
//                 _ofs.open(pathname, std::ios::binary | std::ios::app);
//                 assert(_ofs.is_open());
//             }
//             _ofs.write(data, len);
//             assert(_ofs.good());
//         }

//     private:
//         std::string CreateNewFile() // 进行文件大小的判断，超出滚动文件规定大小，创建新文件
//         {
//             time_t time = util::Date::now();
//             struct tm t;
//             localtime_r(&time, &t);
//             std::stringstream pathname;
//             pathname << _basename;
//             pathname << t.tm_year;
//             pathname << t.tm_mon;
//             pathname << t.tm_wday;
//             pathname << t.tm_hour;
//             pathname << t.tm_min;
//             pathname << t.tm_sec;
//              return pathname.str();
//         }

//     private:
//         std::string _basename; // 滚动文件名称
//         std::ofstream _ofs;    // 控制滚动文件的句柄
//         size_t _max_fsize;     // 滚动文件最大容量
//         size_t _cur_fsize;     // 当前文件的大小
//     };

//     // 简单工厂模式 实现的是根据用户传入参数，实现生产不同的落地方式。
//     // 为了解决简单工厂模式不符合开闭原则，使用了模版的可变参数包，以及完美转发
//     //	支持动态扩展：可以创建任意类型 SinkType 的对象。
//     //	保证类型安全：通过模板参数和完美转发避免不必要的类型转换。
//     //	避免内存泄漏：使用 std::make_shared 代替显式 new。
//     class SinkFactory
//     {
//     public:
//         template <typename SinkType, typename... Args>
//         static LogSink::ptr create(Args &&...args)
//         {
//             return std::make_shared<SinkType>(std::forward<Args>(args)...);
//         }
//     };
// }

// #endif





#ifndef __M_SINK_H__
#define __M_SINK_H__
#include "util.hpp"
#include "message.hpp"
#include<cassert>
#include<fstream>
#include<sstream>
#include <memory>
#include <mutex>

namespace lyzlog{
class LogSink {
    public:
    
        using ptr = std::shared_ptr<LogSink>;
        LogSink() {}
        virtual ~LogSink() {}
        virtual void log(const char *data, size_t len) = 0;
};
class StdoutSink : public LogSink {
    public:
        using ptr = std::shared_ptr<StdoutSink>;
        StdoutSink() = default;
        void log(const char *data, size_t len) {
            std::cout.write(data, len);
        }
};
class FileSink : public LogSink {
    public:
        using ptr = std::shared_ptr<FileSink>;
        FileSink(const std::string &filename):_filename(filename) {
            util::File::createDirectory(util::File::path(filename));
            _ofs.open(_filename, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }
        const std::string &file() {return _filename; }
        void log(const char *data, size_t len) {
            _ofs.write((const char*)data, len);
            if (_ofs.good() == false) {
                std::cout << "日志输出文件失败！\n";
            }
        }
    private:
        std::string _filename;
        std::ofstream _ofs;
};
class RollSink : public LogSink {
public:
    using ptr = std::shared_ptr<RollSink>;

    RollSink(const std::string& basename, size_t max_fsize)
        : _basename(basename), _max_fsize(max_fsize), _cur_fsize(0), _name_count(0) {
        util::File::createDirectory(util::File::path(basename)); // 创建目录
        initLogFile(); // 初始化第一个日志文件
    }

    void log(const char* data, size_t len) override {
        std::lock_guard<std::mutex> lock(_mutex); // 确保线程安全

        // 检查是否需要滚动文件
        if (_cur_fsize + len >= _max_fsize) {
            rollLogFile();
        }

        // 写入日志内容
        _ofs.write(data, len);
        if (!_ofs.good()) {
            std::cerr << "日志输出文件失败！\n";
        }

        // 更新当前文件大小
        _cur_fsize += len;
    }

private:
    void initLogFile() {
        // 创建新的日志文件
        std::string filename = createFilename();
        _ofs.open(filename, std::ios::binary | std::ios::app);
        assert(_ofs.is_open());
        _cur_fsize = 0; // 重置文件大小计数
    }

    void rollLogFile() {
        if (_ofs.is_open()) {
            _ofs.close(); // 关闭当前日志文件
        }
        initLogFile(); // 创建新的日志文件
    }

    std::string createFilename() {
        // 根据时间戳和计数器生成唯一文件名
        time_t t = time(nullptr);
        struct tm lt;
        localtime_r(&t, &lt);

        std::stringstream ss;
        ss << _basename << "_";               // 基础文件名
        ss << (lt.tm_year + 1900) << "_";     // 年
        ss << (lt.tm_mon + 1) << "_";         // 月
        ss << lt.tm_mday << "_";              // 日
        ss << lt.tm_hour << "_";              // 时
        ss << lt.tm_min << "_";               // 分
        ss << lt.tm_sec << "_";               // 秒
        ss << _name_count++ << ".log";        // 计数器，防止重复
        return ss.str();
    }

private:
    std::string _basename;      // 基础文件名
    std::ofstream _ofs;         // 当前日志文件输出流
    size_t _max_fsize;          // 单个日志文件的最大大小
    size_t _cur_fsize;          // 当前日志文件的大小
    size_t _name_count;         // 文件名计数器
    std::mutex _mutex;          // 线程安全的互斥锁
};

class SinkFactory {
    public:
        template<typename SinkType, typename ...Args>
        static LogSink::ptr create(Args &&...args) {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
};

}
#endif